#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include "Util.hpp"
#include "HandlerData.hpp"
#include "HttpServer.hpp"
#include "User.hpp"
#include "deamon.hpp"
#define SUBMIT_URL "url"
#define SUBMIT_NUM "nums"
#define SUBMIT_CODE "code"
#define SUBMIT_SUBMIT "submit"
#define SUBMIT_QUERY "query"

using namespace wyl;

bool CheckUrlValue(const std::string &url)
{
    size_t pos = url.find("https://blog.csdn.net/");
    if (pos == std::string::npos)
    {
        return false;
    }
    return true;
}

bool CheckNumValue(const std::string &value_nums)
{
    if (value_nums.size() > 3)
        return false;
    int num = std::stoi(value_nums);
    if (num <= 0 || num >= 1000)
    {
        // 要求设置正确的数量
        return false;
    }
    return true;
}

bool CheckCode(std::string code)
{
    UserManager* um = UserManager::GetInstance(); 
    logMessage(DEBUG,"begin check code2 ....");
    if(um->GetCodes()->FindCode(code)) 
    {
        um->GetCodes()->SetDeleteCode(code);
        return true;
    }
    return false;
}

enum ReqErr
{
    PARSE_BODY_ERR = 1,
    URL_ERR,
    NUM_ERR,
    CODE_ERR,
    SUBMIT_ERR
};

int HandlerReqBody(const std::string &_body, User *us, std::string &submit_type)
{
    // 解析正文
    // url=xxx&nums=xxx&subitm=xxxx
    logMessage(DEBUG,"begin parse request body....");

    std::unordered_map<std::string, std::string> _map;
    int begin = 0;
    while (1)
    {
        size_t pos1 = _body.find("=", begin);
        if (pos1 == std::string::npos)
            break;
        size_t pos2 = _body.find("&", pos1);
        if (pos2 == std::string::npos)
        {
            _map.insert(std::make_pair(_body.substr(begin, pos1 - begin), _body.substr(pos1 + 1)));
            break;
        }
        // left = 0 - pos1 , right = pos1 + 1 - pos2
        _map.insert(std::make_pair(_body.substr(begin, pos1 - begin), _body.substr(pos1 + 1, pos2 - pos1 - 1)));
        begin = pos2 + 1;
    }
    logMessage(DEBUG,"begin get body ....");

    std::string str_conut;
    std::string str_code;
    // 获取提交链接
    auto it = _map.find(SUBMIT_URL);
    if (it == _map.end())
        return URL_ERR;
    us->SetUrl(it->second);

    // 获取提交数量
    it = _map.find(SUBMIT_NUM);
    if (it == _map.end())
        return NUM_ERR;
    std::string str_num = it->second; 
    if(str_num.size() == 0 || str_num.size() > 3) return NUM_ERR;
    for(auto& e :str_num)
    {
        if(!(e >= '0' && e <= '9')) return NUM_ERR;
    }
    us->SetCount(std::stoi(it->second));
    str_conut = it->second;

    // 获取提交兑换码
    it = _map.find(SUBMIT_CODE);
    if (it == _map.end())
        return CODE_ERR;
    str_code = it->second;

    // 获取submit类型/ 提交 or 查询
    it = _map.find(SUBMIT_SUBMIT);
    if (it != _map.end())
        submit_type = SUBMIT_SUBMIT;
    else if ((it = _map.find(SUBMIT_QUERY)) != _map.end())
        submit_type = SUBMIT_QUERY;
    else
        return SUBMIT_ERR;

    // 校验value_url
    logMessage(DEBUG,"begin check url ....");

    if (!CheckUrlValue(us->GetUrl()))
    {
        // 提示博客地址可能不正确
        return URL_ERR;
    }
    logMessage(DEBUG,"begin check num ....");

    if (!CheckNumValue(str_conut))
    {
        // 提示输入的数超出
        return NUM_ERR;
    }
    logMessage(DEBUG,"begin check code ....");

    if (!CheckCode(str_code))
    {
        return CODE_ERR;
    }
    return 0; 
}
void *Execing(void *args)
{
    pthread_detach(pthread_self()); // 线程分离  5个线程处理http请求，5个线程管理5个执行刷访客的进程
    User* us = (User*)args;
    int pid = fork();
    if(pid == 0)
    {
        prctl(PR_SET_PDEATHSIG,SIGHUP);
        //子进程执行逻辑.....
        execl("./cgi/mypro","./cgi/mypro",us->GetUrl().c_str(),std::to_string(us->GetCount()).c_str(),"60",NULL); //argv[2]是count的字符串形式
        logMessage(ERROR,"execl failed.....");
        exit(0);
    }
    UserManager* um = UserManager::GetInstance();  
    User tmp_us;
    waitpid(pid,nullptr,0);
    um->over(us->GetUrl());
    delete us;
    return nullptr;
}

// 处理有正文的报文，也就是动态资源
void Handler(const Request &req, Response &resp)
{
    // 1.获取请求正文
    std::string _body = req.GetRequestBody();
    // 处理正文
    User *us = new User();
    std::string submit;
    logMessage(NORMAL,"begin HandlerReqBoy....");
    int ret = HandlerReqBody(_body, us, submit); // 成功返回0，且url和num被设置，否则返回错误

    // 根据提交类型调用不同的函数
    std::string resp_path = WEBROOT; // 响应的文件地址
    logMessage(DEBUG, "resp_path : %s \n req_body : %s", resp_path.c_str(), _body.c_str());
    // 构建响应
    if (ret == 0)
    {
        // 根据请求类型做不同的操作,submit操作
        if (submit == SUBMIT_SUBMIT)
        {
            UserManager* um = UserManager::GetInstance();
            // 刷的人是否已经满了(允许刷访客有人数上线)
            if(um->IsFull())
            {
                resp.SetBody(resp_path + "/QUEUEFULL.html");
                resp.SetStatuCode(400);
            }
            else if (um->isexist(us->GetUrl())) // 是否重复提交申请...
            {
                // 已经加入到队列中了
                resp.SetBody(resp_path + "/REURL.html");
                resp.SetStatuCode(400);
            }
            else
            {
                // 请求正常
                //减少兑换码
                um->GetCodes()->DeleteCode();
                //设置状态码
                resp.SetStatuCode(200);
                resp.SetBody(resp_path + "/ok.html");
                um->begin(*us);
                // 后台进行处理.....
                // 创建线程 -> 线程创建子进程 -> 程序替换 -> 线程记录进程执行过程 -> 线程记录进程执行结束
                pthread_t tid;
                pthread_create(&tid, nullptr, Execing, (void *)us);
                logMessage(NORMAL,"create pthread success...");
            }
        }else if(submit == SUBMIT_QUERY)
        {
            //查询操作....
            resp.SetStatuCode(200);
            resp.SetBody(resp_path + "/QUERY.html");
        }
    }
    else if (ret == PARSE_BODY_ERR)
    {
        // 构建一个400请求
        resp.SetStatuCode(400);
        resp.SetBody(resp_path + "/400.html");
    }
    else if (ret == URL_ERR)
    {
        // 构建一个400请求
        resp.SetStatuCode(400);
        resp.SetBody(resp_path + "/URLERR.html");
    }
    else if (ret == NUM_ERR)
    {
        // 构建一个400请求
        resp.SetStatuCode(400);
        resp.SetHeader("Content-Type", ".html");
        resp.SetBody(resp_path + "/NUMERR.html");
    }
    else if (ret == CODE_ERR)
    {
        resp.SetStatuCode(400);
        resp.SetHeader("Content-Type", ".html");
        resp.SetBody(resp_path + "/CODEERR.html");
    }
    // 发送响应
    logMessage(NORMAL, "Send Response ....");
    resp.Send();
    // resp.SetHeader("Content-Type","text/html");
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cout << "Usage :  " << argv[0] << "  serverport" << std::endl;
        exit(USAG_ERR);
    }
    // 提取端口
    uint16_t port = atoi(argv[1]);
    wyl::HttpServer* httpserver = new wyl::HttpServer(Handler, port);
    httpserver->init();
    daemonSelf();
    httpserver->start();
    return 0;
}